package com.oren.config;

import com.oren.annotation.WswAspect;
import com.oren.annotation.WswService;
import com.oren.proxy.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * @author wangshiwei
 * @create 2018/7/7 17:34
 * @desc 方法拦截助手类
 **/
public final class AopHelper {

    private  static final Logger LOGGER= LoggerFactory.getLogger(AopHelper.class);

    static {
        try {
            // 创建 Proxy Map（用于 存放代理类 与 目标类列表 的映射关系）
            Map<Class<?>,Set<Class<?>>> proxyMap=createProxyMap();
            // 创建 Target Map（用于 存放目标类 与 代理类列表 的映射关系）
            Map<Class<?>,List<Proxy>> targetMap=createTargetMap(proxyMap);
            for (Map.Entry<Class<?>,List<Proxy>> targetEntity:targetMap.entrySet()) {
                // 分别获取 map 中的 key 与 value
                Class<?> tarClass=targetEntity.getKey();
                List<Proxy> proxyList=targetEntity.getValue();
                // 创建代理实例
                Object proxy= ProxyManager.createProxy(tarClass,proxyList);
                // 用代理实例覆盖目标实例，并放入 Bean 容器中
                BeanHelper.setBean(tarClass,proxy);
            }
        }catch (Exception e){
            LOGGER.error("aop failure",e);
        }
    }

    /**
     * 创建代理的map
     * @return
     * @throws Exception
     */
    public static Map<Class<?>,Set<Class<?>>> createProxyMap()throws Exception{
        // 获取切面类（所有继承于 BaseAspect 的类）
        Map<Class<?>,Set<Class<?>>> proxyMap=new HashMap<Class<?>, Set<Class<?>>>();
        addAspectProxy(proxyMap);
        addTransactionProxy(proxyMap);
        return proxyMap;
    }


    /**
     * 添加普通切面代理
     * @return
     * @throws Exception
     */
    public static Map<Class<?>,Set<Class<?>>> addAspectProxy(Map<Class<?>,Set<Class<?>>> proxyMap)throws Exception{
        // 获取切面类（所有继承于 BaseAspect 的类）
        //扫描包下面的所有的子类
        Set<Class<?>> proxClassSet=ClassHelper.getClassSetBySuper(AspectProxy.class);
        for (Class<?> proClass: proxClassSet) {
            if(proClass.isAnnotationPresent(WswAspect.class)){
                WswAspect wswAspect=proClass.getAnnotation(WswAspect.class);
                Set<Class<?>> targetClassSet=createTargetClassSet(wswAspect);
                proxyMap.put(proClass,targetClassSet);
            }
        }
        return proxyMap;
    }

    /**
     * 添加事物代理
     * @param proxyMap
     */
    private static void addTransactionProxy(Map<Class<?>,Set<Class<?>>> proxyMap){
        Set<Class<?>> serviceClassSet=ClassHelper.getClassSetByAnnotation(WswService.class);
        proxyMap.put(TransactionProxy.class,serviceClassSet);
    }

    /**
     * 创建切面的class集合
     * @param wswAspect
     * @return
     * @throws Exception
     */
    public static Set<Class<?>> createTargetClassSet(WswAspect wswAspect)throws Exception{
        Set<Class<?>> targetClassSet=new HashSet<Class<?>>();
        Class<? extends Annotation> annotation = wswAspect.value() ;
        if(annotation!=null && !annotation.equals(WswAspect.class)){
            targetClassSet.addAll(ClassHelper.getClassSetByAnnotation(annotation));
        }
        return targetClassSet;
    }

    /**
     * 获取代理的所有方法
     * @param proxyMap
     * @return
     * @throws Exception
     */
    public static Map<Class<?>,List<Proxy>> createTargetMap(Map<Class<?>,Set<Class<?>>> proxyMap)throws Exception{
        Map<Class<?>,List<Proxy>> targetMap=new HashMap<Class<?>, List<Proxy>>();
        for (Map.Entry<Class<?>,Set<Class<?>>> proxyEntity: proxyMap.entrySet()) {
            //代理对象
            Class<?> proxyClass=proxyEntity.getKey();
            //目标对象
            Set<Class<?>> targeteClassSet=proxyEntity.getValue();
            for (Class<?> targetClass: targeteClassSet) {
                Proxy proxy=(Proxy) proxyClass.newInstance();
                if(targetMap.containsKey(targetClass)){
                    targetMap.get(targetClass).add(proxy);
                }else{
                    List<Proxy> proxyList=new ArrayList<Proxy>();
                    proxyList.add(proxy);
                    targetMap.put(targetClass,proxyList);
                }
            }
        }
        return targetMap;
    }

}
